﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "LinearTemporaryDimensionHandler.h"
#include "UiViewUtils.h"
#include "IActionManager.h"
#include "IAction.h"
#include "Vector3dUtils.h"
#include "TemporaryDimensionCommonsUtils.h"
#include "IGraphicsNodeGroup.h"

#include "IUiDocumentViewManager.h"
#include "IUiView.h"
#include "IModelView.h"

#include <QLineEdit>
#include <QIntValidator>
#include <QKeyEvent>

#include "TemporaryDimensionContext.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"


using namespace gcmp;

LinearTemporaryDimensionHandler::LinearTemporaryDimensionHandler(IUiView* pUIView)
    : TemporaryDimensionHandler(pUIView)
    , m_enableLength(true)
    , m_enableAngle(true)
{
}

LinearTemporaryDimensionHandler::~LinearTemporaryDimensionHandler(void)
{
}

bool LinearTemporaryDimensionHandler::event(QEvent* pEvent)
{
    // 此方法应为入口键盘事件的处理函数
    if (pEvent->type() != QEvent::KeyPress)
    {
        return QObject::event(pEvent);
    }
    
    QKeyEvent* pKeyEvent = dynamic_cast<QKeyEvent*>(pEvent);
    DBG_WARN_AND_RETURN_UNLESS(pKeyEvent != nullptr, QObject::event(pEvent), L"难道pEvent不是键盘事件?",L"GDMPLab",L"2024-03-30");
    
    // 视图输入的入口
    // 当前Action启用了绘制助手时, 输入数字，激活视图内输入
    // 仅负责启动状态, 不负责状态启动后的事件处理
    InputState initInputState = InputState::None;
    if (IsLengthEnabled())
    {
        initInputState = InputState::Length;
    }
    else if (IsAngleEnabled())
    {
        initInputState = InputState::Angle;
    }
    else
    {
        DBG_WARN(L"无法设置EnInputState的类型，将按IS_Length设置",L"GDMPLab",L"2024-03-30");
        initInputState = InputState::Length;
    }

    if (IsInInputState() == false)
    {
        if (pKeyEvent->key() >= Qt::Key_A && pKeyEvent->key() <= Qt::Key_Z)
        {
            QString keyStr = pKeyEvent->text();
            ShowInputWidget(initInputState, false, true, keyStr.toStdWString());
            return true;
        }

        switch (pKeyEvent->key())
        {
        // 长度输入启动按键：
        case Qt::Key_0:
        case Qt::Key_1:
        case Qt::Key_2:
        case Qt::Key_3:
        case Qt::Key_4:
        case Qt::Key_5:
        case Qt::Key_6:
        case Qt::Key_7:
        case Qt::Key_8:
        case Qt::Key_9:
            ShowInputWidget(initInputState, false, true, QString::number(pKeyEvent->key() - Qt::Key_0).toStdWString());
            return true;

        case VK_NUMPAD0:
        case VK_NUMPAD1:
        case VK_NUMPAD2:
        case VK_NUMPAD3:
        case VK_NUMPAD4:
        case VK_NUMPAD5:
        case VK_NUMPAD6:
        case VK_NUMPAD7:
        case VK_NUMPAD8:
        case VK_NUMPAD9:
            ShowInputWidget(initInputState, false, true, QString::number(pKeyEvent->key() - VK_NUMPAD0).toStdWString());
            return true;

        // AngleOnly: 角度输入启动按键，可用“-”:
        case Qt::Key_Minus:
        case Qt::Key_onehalf:
        case Qt::Key(109):
            ShowInputWidget(initInputState, false, true, L"-");
            break;            

        // LengthAndAngle: 角度输入启动按键: comma
        case Qt::Key_Comma:
        case Qt::Key_onequarter:
            if (IsLengthEnabled() && IsAngleEnabled())
            {
                if (pKeyEvent->modifiers() & Qt::ShiftModifier)
                {
                    ShowInputWidget(InputState::Angle);
                    return true;
                }
                break;
            }

        default:
            break;
        }

        IUiView* pUIView = IUiDocumentViewManager::Get()->GetUiViewByRuntimeId(m_uiViewId);
        IActionManager::RefreshViewInput(pUIView);
    }
    return QObject::event(pEvent);
}


bool LinearTemporaryDimensionHandler::eventFilter(QObject* obj, QEvent* pEvent)
{
    if (pEvent->type() == QEvent::KeyPress)
    {
        QKeyEvent* pKeyEvent = dynamic_cast<QKeyEvent*>(pEvent);
        if (pKeyEvent)
        {
            IUiView* pUIView = IUiDocumentViewManager::Get()->GetUiViewByRuntimeId(m_uiViewId);
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pUIView != nullptr, L"pUIView指针不能为空",L"GDMPLab",L"2024-03-30");
            QWidget* pViewWidget = QWidget::find((WId)pUIView->GetUiViewId());
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pViewWidget != nullptr, L"pViewWidget指针不能为空",L"GDMPLab",L"2024-03-30");
            
            switch (pKeyEvent->key())
            {
            case Qt::Key_Return:// 日志敏感
            case Qt::Key_Enter:// 日志敏感
            case Qt::Key_Space:
            {
                OnEditorReturnPressed();
                return false; //注意：返回false能让错误输入焦点停留在输入框，修改注意检查错误输入后的回车键
            }

            case Qt::Key_Escape:
                HideInputWidget();
                if (m_InputState != InputState::None)
                {
                    return true;
                }
                return false;

            case Qt::Key_Tab:
                if (IsLengthEnabled() && IsAngleEnabled())
                {
                    OnEditorTabPressed();
                    return true; // 切换输入状态, 拦截KeyEvent, 防止输入焦点被抢走
                }
            default:
                break;
            }
        }
    }
    else if (pEvent->type() == QEvent::KeyRelease)
    {
        return true;
    }

    return QObject::eventFilter(obj, pEvent);
}

gcmp::OwnerPtr<gcmp::IGraphicsNodeGroup> LinearTemporaryDimensionHandler::GenerateGraphics(const gcmp::ITemporaryDimensionContext* pCtx)
{
    OwnerPtr<IGraphicsNodeGroup> opGrepGroup = IGraphicsNodeGroup::Create();
    opGrepGroup->SetIsSnappable(false);

    DBG_WARN_AND_RETURN_UNLESS(pCtx, opGrepGroup, L"pCtx为空",L"GDMPLab",L"2024-03-30");
    const LinearTemporaryDimensionContext* pLinearCtx = dynamic_cast<const LinearTemporaryDimensionContext*>(pCtx);
    DBG_WARN_AND_RETURN_UNLESS(pLinearCtx, opGrepGroup, L"pLinearCtx为空",L"GDMPLab",L"2024-03-30");
    
    Vector3d firstDockPos = pLinearCtx->GetStartPt();
    Vector3d secondDockPos = pLinearCtx->GetEndPt();
    Coordinate3d coord = pLinearCtx->GetCoord();
    SetUserCustomInput(pLinearCtx->IsUserCustomInput());
    m_TmpDimStyle.LengthPrecision = pLinearCtx->GetLengthPrecision();
    m_TmpDimStyle.AnglePrecision = pLinearCtx->GetAnglePrecision();
    m_TmpDimStyle.RadiusPrecision = pLinearCtx->GetRadiusPrecision();

    // 若始点与终点一致，则不绘制临时标注
    if(Vector3dUtils::IsEqual(firstDockPos, secondDockPos))
    {
        return opGrepGroup;
    }

    UpdateUiView();

    IUiView* pUIView = IUiDocumentViewManager::Get()->GetUiViewByRuntimeId(m_uiViewId);
    DBG_WARN_AND_RETURN_UNLESS(pUIView, opGrepGroup, L"pUIView为空！",L"GDMPLab",L"2024-03-30");
    IModelView* pModelView = pUIView->GetModelView();
    DBG_WARN_AND_RETURN_UNLESS(pModelView, opGrepGroup, L"pModelView为空！",L"GDMPLab",L"2024-03-30");

    double pixelLength = UiViewUtility::ComputeWorldWidthFromPixel(pUIView, 1);

    if (IsLengthEnabled())
    { // 长度标注
        OwnerPtr<IGraphicsNodeGroup> opLengthDimensions = TemporaryDimensionCommonsUtils::GenerateLengthDimension(
            firstDockPos, secondDockPos, coord,
            m_InputState, m_TmpDimStyle, pixelLength, m_TmpDimData);
        opGrepGroup->AddChild(TransferOwnership(opLengthDimensions));
    }

    if (IsAngleEnabled())
    { // 角度标注
        OwnerPtr<IGraphicsNodeGroup> opAngleDimensions = TemporaryDimensionCommonsUtils::GenerateAngleDimension(
            firstDockPos, secondDockPos, coord,
            m_InputState, m_TmpDimStyle, pixelLength, m_TmpDimData);
        opGrepGroup->AddChild(TransferOwnership(opAngleDimensions));
    }

    return opGrepGroup;
}

void LinearTemporaryDimensionHandler::ShowInputWidget(InputState enInputState, bool isTabShift /*= false*/, bool isOverrideValue /*= false*/, std::wstring inputValue/* = L""*/)
{
    bool isInputStateValid = (enInputState == InputState::Length || enInputState == InputState::Angle);
    DBG_WARN_AND_RETURN_VOID_UNLESS(isInputStateValid, L"InputState输入非法!",L"GDMPLab",L"2024-03-30");

    m_InputState = enInputState;
    if (m_InputState != InputState::None)
    {
        // 创建输入框
        if (!isTabShift)
        {
            CreateEditorWidget(); 
        }

        // 配置输入合法性检查
        if (!IsUserCustomInput())
        {
            std::unordered_map<const QLineEdit*, const ValidatorBase*> validators;
            if (m_InputState == InputState::Length)
            {
                validators.insert(std::make_pair(m_pEditor, &m_lenValidator));
            }
            else if (m_InputState == InputState::Angle)
            {
                validators.insert(std::make_pair(m_pEditor, &m_angValidator));
            }
            else
            {
                DBG_WARN(L"InputState输入非法!",L"GDMPLab",L"2024-03-30");
            }
            m_opValidateHelper->SetValidators(validators);
        }


        Vector3d editorWorldPos;
        switch (m_InputState)
        {
        case InputState::Length:
        {
            editorWorldPos = m_TmpDimData.LengthDimensionPosition;

            if (isTabShift)
            {
                if (isOverrideValue)
                {
                    m_pEditor->setText(QString::fromStdWString(inputValue));
                }
                else
                {
                    m_pEditor->setText(QString::number(m_TmpDimData.LengthValue, 'f', m_TmpDimStyle.LengthPrecision));
                }
                m_pEditor->selectAll();
            }
            else
            {
                m_pEditor->setText(QString::fromStdWString(inputValue));
            }
            break;
        }
        case InputState::Angle:
        {
            editorWorldPos = m_TmpDimData.AngleDimensionPosition;

            if (isOverrideValue)
            {
                m_pEditor->setText(QString::fromStdWString(inputValue));
            }
            else
            {
                m_pEditor->setText(QString::number(m_TmpDimData.AngleValue, 'f', m_TmpDimStyle.AnglePrecision));
            }
            if (IsLengthEnabled() && IsAngleEnabled())
            {
                m_pEditor->selectAll();
            }
            break;
        }

        default: break;
        }

        // 显示输入框
        m_pEditor->show();
        m_pEditor->setFocus();

        // 设置输入框位置
        SetEditorPosition(editorWorldPos);

        // 刷新当前视图
        IUiView* pUIView = IUiDocumentViewManager::Get()->GetUiViewByRuntimeId(m_uiViewId);
        IActionManager::RefreshViewInput(pUIView);
    }
}

bool LinearTemporaryDimensionHandler::WakeOn(int nKeyChar, bool isCtrlPressed /*= false*/, bool isAltPressed /*= false*/, bool isShiftPressed /*= false*/)
{
    // 如果不是数字, 不是comma，不是minus,不是字符串, 则不处理
    bool isNumKey = (Qt::Key_0 <= nKeyChar && nKeyChar <= Qt::Key_9);
    bool isNumPadKey = (VK_NUMPAD0 <= nKeyChar && nKeyChar <= VK_NUMPAD9);
    bool isCommaKey = (nKeyChar == VK_OEM_COMMA);
    bool isMinusKey = (nKeyChar == VK_OEM_MINUS) || (nKeyChar == VK_SUBTRACT);
    bool isCharKey = (IsUserCustomInput() ? (Qt::Key_A <= nKeyChar && nKeyChar <= Qt::Key_Z) : false);
    if (!isNumKey && !isNumPadKey && !isCommaKey && !isMinusKey && !isCharKey)
    {
        return false;
    }

    Qt::Key key = (Qt::Key)nKeyChar;

    // 为了让验证器支持快捷键（Alt+P), 临时在这里修改
    if (isAltPressed && key == Qt::Key_P)
        return false;

    Qt::KeyboardModifiers modifiers = Qt::NoModifier;
    if (isCtrlPressed)
    {
        modifiers |= Qt::ControlModifier;
    }
    if (isAltPressed)
    {
        modifiers |= Qt::AltModifier;
    }
    if (isShiftPressed)
    {
        modifiers |= Qt::ShiftModifier;
    }
   
    //////////////////////////////////////////////////////////////////////////
    // 虚键值转字符
    std::string strSrc;
    if (isNumKey || isCharKey)
    {
        char c = toascii(nKeyChar);
        strSrc.push_back(c);
        transform(strSrc.begin(), strSrc.end(), strSrc.begin(), tolower);  
    }
    else if(isNumPadKey)
    {
        strSrc = std::to_string(nKeyChar - VK_NUMPAD0);
    }
    else
    {
        switch (nKeyChar)
        {
        case VK_SUBTRACT:
        case VK_OEM_MINUS: strSrc = "-"; break;
        default: break;
        }
    }

    QKeyEvent keyEvent(QEvent::KeyPress, key, modifiers, QString::fromStdString(strSrc));
    return event(&keyEvent);
}

bool gcmp::LinearTemporaryDimensionHandler::UpdateConfig(const ITemporaryDimensionContext * pCtx)
{
    if (!this->TemporaryDimensionHandler::UpdateConfig(pCtx))
    {
        return false;
    }

    const LinearTemporaryDimensionContext* pLinearCtx = dynamic_cast<const LinearTemporaryDimensionContext*>(pCtx);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pLinearCtx, L"pLinearCtx为空。",L"GDMPLab",L"2024-03-30");
    this->SetLengthEnabled(pLinearCtx->IsLengthDimentionEnabled());
    this->SetAngleEnabled(pLinearCtx->IsAngleDimentionEnabled());

    return true;
}

void LinearTemporaryDimensionHandler::OnEditorReturnPressed()
{
    if (!IsInInputState())
    {
        return;
    }

    ActionViewInput inputParam;
    bool isDouble = false;
    bool isValidate = false;
    double digitVal = m_pEditor->text().toDouble(&isDouble);
    inputParam.InputVal = digitVal;
    if (IsUserCustomInput())
    {
        inputParam.InputString = m_pEditor->text().toStdWString();
        m_opValidateHelper.reset(nullptr);
    }

    if (m_InputState == InputState::Length)
    {
        // 输入合法性检查
        if (!IsUserCustomInput())
        {
            std::unordered_map<const QLineEdit*, const ValidatorBase*> validators;
            validators.insert(std::make_pair(m_pEditor, &m_lenValidator));
            m_opValidateHelper->SetValidators(validators);
        }

        isValidate = m_lenValidator.ValidateValue(digitVal);
        inputParam.InputType = ActionViewInputType::Confirm_Length;
    }
    else if(m_InputState == InputState::Angle)
    {
        // 输入合法性检查
        if (!IsUserCustomInput())
        {
            std::unordered_map<const QLineEdit*, const ValidatorBase*> validators;
            validators.insert(std::make_pair(m_pEditor, &m_angValidator));
            m_opValidateHelper->SetValidators(validators);
        }

        isValidate = m_angValidator.ValidateValue(digitVal);
        inputParam.InputType = ActionViewInputType::Confirm_Angle;
    }
    else
    {
        DBG_WARN(L"m_InputState怎么不是IS_Length或IS_Angle?",L"GDMPLab",L"2024-03-30");
    }

    // 忽略非法输入
    if ( !IsUserCustomInput() && (!isDouble || !isValidate))
    {
        m_pEditor->selectAll();
        return;
    }
    else
    {
        UpdateUiView();
        HideInputWidget();
    }

    // 这个方法会结束当前的IPickPointAction!! 连带结束当前Helper的生命周期!!! 后续不应该再添加任何代码!
    IUiView* pUIView = IUiDocumentViewManager::Get()->GetUiViewByRuntimeId(m_uiViewId);
    IActionManager::ProcessViewInput(pUIView, inputParam);
}

void LinearTemporaryDimensionHandler::OnEditorTabPressed()
{
    m_pEditor->clearFocus();
    
    InputState switchType = InputState::None;
    ActionViewInput inputParam;
    bool isDouble = false;
    bool isValidate = false;
    double digitVal = m_pEditor->text().toDouble(&isDouble);
    inputParam.InputVal = digitVal;
    if(IsUserCustomInput())
    {
        inputParam.InputString = m_pEditor->text().toStdWString();
        m_opValidateHelper.reset(nullptr);
    }

    if(m_InputState == InputState::Length)
    {
        // 输入合法性检查
        if (!IsUserCustomInput())
        {
            std::unordered_map<const QLineEdit*, const ValidatorBase*> validators;
            validators.insert(std::make_pair(m_pEditor, &m_lenValidator));
            m_opValidateHelper->SetValidators(validators);
        }

        inputParam.InputType = ActionViewInputType::Preview_Length;
        isValidate = m_lenValidator.ValidateValue(digitVal);
        switchType = InputState::Angle;  // 切换角度输入
    }
    else if (m_InputState == InputState::Angle)
    {
        // 输入合法性检查
        if(!IsUserCustomInput())
        {
            std::unordered_map<const QLineEdit*, const ValidatorBase*> validators;
            validators.insert(std::make_pair(m_pEditor, &m_angValidator));
            m_opValidateHelper->SetValidators(validators);
        }

        inputParam.InputType = ActionViewInputType::Preview_Angle;
        isValidate = m_angValidator.ValidateValue(inputParam.InputVal);
        switchType = InputState::Length;  // 切换长度输入      
    }
    else
    {
        DBG_WARN(L"m_InputState怎么不是IS_Length或IS_Angle?",L"GDMPLab",L"2024-03-30");
    }

    // 忽略非法输入
    if (!IsUserCustomInput() && (!isDouble || !isValidate) )
    {
        m_pEditor->selectAll();
        return;
    }

    IUiView* pUIView = IUiDocumentViewManager::Get()->GetUiViewByRuntimeId(m_uiViewId);
    IActionManager::ProcessViewInput(pUIView, inputParam);

    // 取消非法检查
    {
        std::unordered_map<const QLineEdit*, const ValidatorBase*> validators;
        validators.insert(std::make_pair(m_pEditor, nullptr));
        m_opValidateHelper->SetValidators(validators);
    }
   
    ShowInputWidget(switchType, true);
    
}
